/*
 * Change Logs:
 * Date           Author       Notes
 * 2020-10-15     chenbin
 * 2020-11-13     chenbin
 */
#include "board.h"
#include "fboot.h"

#ifdef RT_USING_DFS
#include <dfs.h>
#include <dfs_fs.h>
#include <dfs_file.h>

#include <unistd.h>
#include <dirent.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#endif

#ifdef RT_USING_SPI
#include "drv_spi.h"
#endif
#ifdef RT_USING_SFUD
#include "spi_flash_sfud.h"
#endif

#undef DBG_TAG
#undef DBG_LVL
#define DBG_TAG "flash"
#define DBG_LVL DBG_LOG
#include <rtdbg.h>

int hw_spi_flash_init(void)
{
	__HAL_RCC_GPIOB_CLK_ENABLE();
	rt_hw_spi_device_attach("spi1", "spi10", GPIOB, GPIO_PIN_14);

	if (RT_NULL == rt_sfud_flash_probe("W25Q128", "spi10"))
	{
		return -RT_ERROR;
	};
	return RT_EOK;
}

int board_flash_init(void)
{
	hw_spi_flash_init();

	fal_init();
	fal_show_part_table();

	fal_blk_device_create("fs");

	return RT_EOK;
}

void filesysytem_try_mount(char *device_name, char *mount_point, char *fs_type_name, int mkfs_count);
//#include "drv_ramdisk.h"
//#define RAMDISK_SIZE 256 * 1024

int board_filesystem_init(void)
{
//	ramdisk_init("ramdisk0", NULL, 512, RAMDISK_SIZE / 512);
//	dfs_mkfs("elm", "ramdisk0");
//	int rc = dfs_mount("ramdisk0", "/", "elm", 0, 0);
//	if (rc == 0)
//	{
//		rt_kprintf("mounted ramdisk0 on / \n");
//		filesysytem_try_mount("fs", "/fs", "elm", 1);
//#if defined(RT_USING_DFS) && defined(RT_USING_SDIO)
//		int result = mmcsd_wait_cd_changed(RT_TICK_PER_SECOND);
//		if (result == MMCSD_HOST_PLUGED)
//		{
//			filesysytem_try_mount("sd0", "/sd", "elm", 0);
//		}
//#endif
//	}
//	else
//	{
//		rt_kprintf("ramdisk0 mount failed %d \n", rc);
//	}
	
	filesysytem_try_mount("fs", "/", "elm", 1);
	
	mkdir("/config", 0);
	
	return RT_EOK;
}

extern void board_show_info(void);
extern int board_key_get(int index);
extern char *board_unique_id_str(void);

#include "fboot.h"
#include "fupdate.h"

int fboot_get_key_status(void)
{
	return board_key_get(0);
}

int fupdate_aes256_update(const char *aes_iv, const char *aes_key);

void bootloader_fboot_main(void)
{
#if (FUPDATE_USING_AES256 == 1)
	fupdate_aes256_update("123456789ABCDEF0","123456789ABCDEF0123456789ABCDEF0"); // update aes256 key iv
#endif

	int count = fboot_wait_key(3);
	if (count > 0)
	{
		rt_kprintf("fboot stop\n");
	}
	else
	{
		fupdate_fal2fal("down", NULL, 1); // check update
//		fload_fal("app", 0x80000000U, 0x00200000U, 0x00U);
//		fboot_addr(0x80000000U);
		fboot_fal("app", 0x00U);
	}
}

#if (FBOOT_USING_BOOTLOADER == 1)

typedef void (*pFunction)(void);
#define USER_BOOT_ADDR 0x08000000
#define USER_APP_ADDR 0x08020000
#define NVIC_VTOR_MASK 0x3FFFFF80

RT_WEAK int fboot_jump_app(uint32_t app_addr)
{
	uint32_t app_entry_stack;
	pFunction app_entry_func;

	app_entry_stack = (*(volatile uint32_t *)app_addr);
	app_entry_func = (pFunction)(*(volatile uint32_t *)(app_addr + 4));

	rt_kprintf("call:0x%08X\n", app_addr);
	rt_kprintf("msp :0x%08X\n", app_entry_stack);
	rt_kprintf("pc  :0x%08X\n", app_entry_func);
	rt_kprintf("\n\n\n");
	if ((app_entry_stack & 0x2FFC0000) == 0x20000000)
	{
#ifdef SCB_DisableICache
		SCB_DisableICache();
#endif
#ifdef SCB_DisableDCache
		SCB_DisableDCache();
#endif
		__disable_irq();
    for (IRQn_Type irq = WWDG_IRQn; irq <= FPU_IRQn; irq++)
    {
        NVIC_DisableIRQ(irq);
    }

		SysTick->CTRL = 0;
		SysTick->LOAD = 0;
		SysTick->VAL = 0;

		SCB->VTOR = app_addr & NVIC_VTOR_MASK;

		/* Initialize user application's Stack Pointer */
		__set_MSP(app_entry_stack);
		/* Jump to user application */
		app_entry_func();

		SCB->VTOR = USER_BOOT_ADDR & NVIC_VTOR_MASK;
		//__enable_irq();
	}
	rt_kprintf("call fail !!!\n\n\n");
	return 0;
}

#endif




#if defined(RT_USING_DFS)

void filesysytem_try_mount(char *device_name, char *mount_point, char *fs_type_name, int mkfs_count)
{
	struct statfs fs_stat;
	int rc = 0;

	rt_kprintf("mount(\"%s\",\"%s\",\"%s\");\n", device_name, mount_point, fs_type_name);

	if (rt_device_find(device_name) == NULL)
	{
		rt_kprintf("%s not find!!!\n", device_name);
		return;
	}
	mkdir(mount_point, 0);
_remount:
	rc = dfs_mount(device_name, mount_point, fs_type_name, 0, 0);
	if (rc == 0)
	{
		rt_kprintf("mounted %s on %s\n", device_name, mount_point);
		if (statfs(mount_point, &fs_stat) >= 0)
		{
			rt_kprintf("%s size:%d, total: %d, free: %d \n", mount_point,
								 fs_stat.f_bsize, fs_stat.f_blocks, fs_stat.f_bfree);
		}
	}
	else
	{
		if (mkfs_count > 0)
		{
			rt_kprintf("[%s]try mkfs -t %s %s \n", mkfs_count, fs_type_name, device_name);
			dfs_mkfs(fs_type_name, device_name);
			mkfs_count--;
			goto _remount;
		}
		rt_kprintf("mount failed :%d \n", rc);
	}
}

#endif

#define RTT_CMD_MOUNT 0
#define RTT_CMD_UNMOUNT 0

#if defined(RT_USING_DFS) && defined(RT_USING_FINSH) && defined(FINSH_USING_MSH)

#if RTT_CMD_MOUNT

extern struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX];
/*
 * If no argument is specified, display the mount history;
 * If there are 3 arguments, mount the filesystem.
 * The order of the arguments is:
 * argv[1]: device name
 * argv[2]: mountpoint path
 * argv[3]: filesystem type
 */
int mount(int argc, char *argv[])
{
	if (argc == 1) /* display the mount history */
	{
		struct dfs_filesystem *iter;

		rt_kprintf("filesystem  device  mountpoint\n");
		rt_kprintf("----------  ------  ----------\n");
		for (iter = &filesystem_table[0];
				 iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
		{
			if ((iter != NULL) && (iter->path != NULL))
			{
				rt_kprintf("%-10s  %-6s  %-s\n",
									 iter->ops->name, iter->dev_id->parent.name, iter->path);
			}
		}
		return 0;
	}
	else if (argc == 4)
	{ /* mount a filesystem to the specified directory */
		char *device = argv[1];
		char *path = argv[2];
		char *fstype = argv[3];

		rt_kprintf("mount device %s(%s) onto %s ... ", device, fstype, path);
		if (dfs_mount(device, path, fstype, 0, 0) == 0)
		{
			rt_kprintf("succeed!\n");
			return 0;
		}
		else
		{
			rt_kprintf("failed!\n");
			return -1;
		}
	}
	else
	{
		rt_kprintf("Usage: mount <device> <mountpoint> <fstype>.\n");
		return -1;
	}
}
MSH_CMD_EXPORT(mount, mount<device><mountpoint><fstype>);
#endif

#if RTT_CMD_UNMOUNT
/* unmount the filesystem from the specified mountpoint */
int unmount(int argc, char *argv[])
{
	if (argc != 2)
	{
		rt_kprintf("Usage: unmount <mountpoint>.\n");
		return -1;
	}

	char *path = argv[1];

	rt_kprintf("unmount %s ... ", path);
	if (dfs_unmount(path) < 0)
	{
		rt_kprintf("failed!\n");
		return -1;
	}
	else
	{
		rt_kprintf("succeed!\n");
		return 0;
	}
}
MSH_CMD_EXPORT(unmount, unmount the mountpoint);
#endif

#endif /* defined(RT_USING_FINSH) && defined(FINSH_USING_MSH) */
